#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <errno.h>
#include <string.h>

#include "hdb.h"
#include "hmm.h"

/* ALIGN() is only to be used to align on a power of 2 boundary */
#define ALIGN(size, boundary)     (((size) + ((boundary) - 1)) & ~((boundary) - 1))

/** Default alignment */
#define ALIGN_DEFAULT(size) ALIGN(size, 8)

///hmm: test mem pool
int test_hmm_pool()
{
    const int buf_size = 1024*10;
    char* buf = malloc(buf_size);
    hmm_t* mm = hmm_new(buf, buf_size);
    char* ptr_arr[10] = {0};

    ptr_arr[0] = hmm_malloc(mm, 4);
    strcpy(ptr_arr[0], "abc");
    hmm_free(mm, ptr_arr[0]);
    hmm_dump(mm, stderr);

    ptr_arr[1] = hmm_malloc(mm, 3);
    strcpy(ptr_arr[1], "def");
    hmm_free(mm, ptr_arr[1]);
    hmm_dump(mm, stderr);

    ptr_arr[2] = hmm_malloc(mm, 5);
    strcpy(ptr_arr[2], "abc");
    hmm_free(mm, ptr_arr[2]);
    hmm_dump(mm, stderr);

    ptr_arr[3] = hmm_malloc(mm, 9);
    strcpy(ptr_arr[3], "abc");
    hmm_free(mm, ptr_arr[3]);
    hmm_dump(mm, stderr);

    assert(ptr_arr[0] == ptr_arr[1]);
    assert(ptr_arr[0] != ptr_arr[2]);
    assert(ptr_arr[2] == ptr_arr[3]);

    hmm_delete(mm);
    return 0;
}

int test_hmm_bigmem()
{
    const int buf_size = 4104 + ALIGN_DEFAULT(4 + 8192) + ALIGN_DEFAULT(4 + 10000) + ALIGN_DEFAULT(4+100);
    char* buf = malloc(buf_size);
    hmm_t* mm = hmm_new(buf, buf_size);
    char* ptr_arr[10] = {0};
    
    ptr_arr[0] = hmm_malloc(mm, 8192);
    strcpy(ptr_arr[0], "first");
    //hmm_free(mm, ptr_arr[0]);
    hmm_dump(mm, stderr);
    
    ptr_arr[1] = hmm_malloc(mm, 10000);
    strcpy(ptr_arr[1], "second");
    //hmm_free(mm, ptr_arr[1]);
    hmm_dump(mm, stderr);
    
    ptr_arr[2] = hmm_malloc(mm, 8192);
    assert(ptr_arr[2] == 0);
    //strcpy(ptr_arr[2], "fail");
    //hmm_free(mm, ptr_arr[2]);
    hmm_dump(mm, stderr);
    
    hmm_delete(mm);
    return 0;
}

const uint32_t mem_size = 4104 + ALIGN_DEFAULT(4 + 8192) + ALIGN_DEFAULT(4 + 10000) + ALIGN(ALIGN_DEFAULT(4+8192), 4096);

int resize(void* ctx, uint32_t newsize)
{
    if (newsize > mem_size)
    {
        fprintf(stderr, "mem bad! memsize=%d newsize=%d\n", mem_size, newsize);
        return -1;
    }
    else
    {
        fprintf(stderr, "mem resize ok. memsize=%d newsize=%d\n", mem_size, newsize);
        return 0;
    }
}

int test_hmm_resize()
{
    const int buf_size = 4104 + ALIGN_DEFAULT(4 + 8192) + ALIGN_DEFAULT(4 + 10000);
    char* buf = malloc(mem_size);
    hmm_t* mm = hmm_new(buf, buf_size);
    char* ptr_arr[10] = {0};

    //hmm_enable_resize(mm, resize, 0);
    
    ptr_arr[0] = hmm_malloc(mm, 8192);
    strcpy(ptr_arr[0], "first");
    //hmm_free(mm, ptr_arr[0]);
    //hmm_dump(mm, stderr);
    
    ptr_arr[1] = hmm_malloc(mm, 10000);
    strcpy(ptr_arr[1], "second");
    //hmm_free(mm, ptr_arr[1]);
    hmm_dump(mm, stderr);
    
    ptr_arr[2] = hmm_malloc(mm, 8192);
    assert(ptr_arr[2] == 0);
    hmm_dump(mm, stderr);

    hmm_enable_resize(mm, &resize, 0);
    
    ptr_arr[2] = hmm_malloc(mm, 8192);
    assert(ptr_arr[2]);
    //hmm_dump(mm, stderr);

    ptr_arr[3] = hmm_malloc(mm, 8192);
    assert(ptr_arr[3] == 0);
    
    hmm_delete(mm);
    return 0;
}

int test_hmm_malloc()
{
    const int buf_size = 4104 + ALIGN_DEFAULT(4 + 4) + ALIGN_DEFAULT(4 + 12) + ALIGN_DEFAULT(4+4092);
    char* buf = malloc(buf_size);
    hmm_t* mm = hmm_new(buf, buf_size);
    char* ptr_arr[10] = {0};
    
    ptr_arr[0] = hmm_malloc(mm, 4);
    strcpy(ptr_arr[0], "first");
    
    ptr_arr[1] = hmm_malloc(mm, 12);
    strcpy(ptr_arr[1], "second");
    
    ptr_arr[2] = hmm_malloc(mm, 4092);
    strcpy(ptr_arr[2], "other");
    

    ptr_arr[3] = hmm_malloc(mm, 4);
    assert(ptr_arr[3] == 0);
    hmm_free(mm, ptr_arr[1]);
    ptr_arr[3] = hmm_malloc(mm, 4);
    assert(ptr_arr[3] != 0);

    
    //hmm_free(mm, ptr_arr[2]);
    hmm_dump(mm, stderr);
    
    hmm_delete(mm);
    return 0;
}

int main(int argc, char* argv[])
{
    //test_hmm_pool();
    //test_hmm_bigmem();
    //test_hmm_resize();
    test_hmm_malloc();
    
    return 0;
}
